﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ASKM.Rachao.Model.Exceptions;
using ASKM.Rachao.Model.Helper;
using ASKM.Rachao.Model.Extensions;
using System.Net.Mail;
using System.Configuration;

namespace ASKM.Rachao.Model.Domain
{
    /// <summary>
    /// Domain classes to validate profile
    /// </summary>
    public class Profile: BaseDomain
    {
        #region const

        public const int STATUS_PENDENTE = 1;
        public const int STATUS_APROVADO = 2;
        public const int STATUS_REPROVADO = 3;
        public const int STATUS_BLOQUEADO = 4;

        #endregion

        #region Methods

        /// <summary>
        /// Returns ProfileStatus pendente
        /// </summary>
        /// <returns>ProfileStatus</returns>
        public ProfileStatus GetProfileStatusPendente()
        {
            return this.CtxRachao.ProfileStatus.FirstOrDefault(x=> x.ProfileStatusId.Equals(STATUS_PENDENTE));
        }

        /// <summary>
        /// Returns ProfileStatus aprovado
        /// </summary>
        /// <returns>ProfileStatus</returns>
        public ProfileStatus GetProfileStatusAprovado()
        {
            return this.CtxRachao.ProfileStatus.FirstOrDefault(x => x.ProfileStatusId.Equals(STATUS_APROVADO));
        }

        /// <summary>
        /// Returns ProfileStatus bloqueado
        /// </summary>
        /// <returns>ProfileStatus</returns>
        public ProfileStatus GetProfileStatusBloqueado()
        {
            return this.CtxRachao.ProfileStatus.FirstOrDefault(x => x.ProfileStatusId.Equals(STATUS_BLOQUEADO));
        }

        /// <summary>
        /// Returns ProfileStatus reprovado
        /// </summary>
        /// <returns>ProfileStatus</returns>
        public ProfileStatus GetProfileStatusReprovado()
        {
            return this.CtxRachao.ProfileStatus.FirstOrDefault(x => x.ProfileStatusId.Equals(STATUS_REPROVADO));
        }

        /// <summary>
        /// Step 1 for initial register
        /// </summary>
        /// <param name="pp">PlayerProfile</param>
        /// <returns>
        /// Player profile id
        /// </returns>
        public int InitialRegister(PlayerProfile pp)
        {

            var ppRegistered = this.GetPlayerProfileByUserNamePassword(
                                    pp.UserAccessProfile.FirstOrDefault().Username, 
                                    pp.UserAccessProfile.FirstOrDefault().Password);

            if (ppRegistered != null)
                return ppRegistered.PlayerProfileId;

            pp.Enabled = true;                        
            
            var ps = this.GetProfileStatusPendente();
            if (ps != null)
            {
                PlayerProfileStatus pps = new PlayerProfileStatus();
                pps.LastStatusDate = DateTime.Now;
                pps.ProfileStatusId = ps.ProfileStatusId;
                pp.PlayerProfileStatus.Add(pps);
            }

            pp.Validate();
            if (pp.UserAccessProfile.Count > 0)
                pp.UserAccessProfile.ToList().ForEach(x => { x.Validate(); x.Enabled = true; x.Password = Intentor.Utilities.CryptoHelper.Encrypt(x.Password); });

            if (this.ExistsPlayerProfileByEmail(pp.Email))
                throw new RachaoBusinessException(false,String.Format(ASKM.Rachao.Resources.Model.Validation.PlayerProfile_Already_Registered,pp.Email));

            if (this.ExistsUserAccessProfileByUserName(pp.UserAccessProfile.First().Username))
                throw new RachaoBusinessException(false, String.Format(ASKM.Rachao.Resources.Model.Validation.UserAccessProfile_Already_Registered, pp.UserAccessProfile.First().Username));

            // approval ticket
            var ppat = this.GetNewPlayerProfileApprovalTicket(); 
            pp.PlayerProfileApprovalTicket.Add(ppat);

            // save changes
            this.CtxRachao.PlayerProfile.Add(pp);
            this.CtxRachao.SaveChanges();

            // send approval message
            using (var communication = new Communication())
            {
                communication.SendApprovalMessage(pp);
            }
            
            return pp.PlayerProfileId;
        }

        /// <summary>
        /// Gets a new player profile approval ticket
        /// </summary>
        /// <returns></returns>
        private PlayerProfileApprovalTicket GetNewPlayerProfileApprovalTicket()
        {
            var ppat = new PlayerProfileApprovalTicket();
            ppat.Enabled = true;
            ppat.GeneratedDate = DateTime.Now.ToFirstHour();
            ppat.ValidDate = ppat.GeneratedDate.AddDays(ConfigHelper.GetValidDaysForApprovalTicket()).ToLastHour();
            return ppat;
        }

        /// <summary>
        /// Verify if there is any player profile by e-mail
        /// </summary>
        /// <param name="email">E-mail</param>
        /// <returns>
        /// True - exists a player profile by email
        /// False - doesnt exist a player profile by email
        /// </returns>
        public bool ExistsPlayerProfileByEmail(string email)
        {
            return this.CtxRachao.PlayerProfile.Any(x=> x.Email.Equals(email));
        }

        /// <summary>
        /// Verify if there is any user access profile by username
        /// </summary>
        /// <param name="username">Username</param>
        /// <returns>
        /// True - exists a user access profile by username
        /// False - doesnt exist a user access profile by username
        /// </returns>
        public bool ExistsUserAccessProfileByUserName(string username)
        {
            return this.CtxRachao.UserAccessProfile.Any(x => x.Username.Equals(username));
        }

        /// <summary>
        /// Gets a player profile by username and password
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="password">Password</param>
        /// <returns>PlayerProfile</returns>
        public PlayerProfile GetPlayerProfileByUserNamePassword(string username, string password)
        {
            string encryptedPassword = Intentor.Utilities.CryptoHelper.Encrypt(password);
            return (from x in this.CtxRachao.PlayerProfile.Include("UserAccessProfile")
                    where x.Email.Equals(username) && x.UserAccessProfile.FirstOrDefault(y => y.Username.Equals(username) && y.Password.Equals(encryptedPassword)) != null
                    select x).FirstOrDefault();

        }

        /// <summary>
        /// Change profile status based on ticket
        /// </summary>
        /// <param name="ticket">PlayerProfileApprovalTicket</param>
        /// <param name="isApproved">Is approved?</param>
        public void ChangeProfileStatusByTicket(PlayerProfileApprovalTicket ticket, bool isApproved)
        {
            // somente processa caso o ultimo status seja igual a pendente
            if (IsPlayerProfileStatusPendente(ticket.PlayerProfileId))
            {
                // if playerprofile id is valid
                if (!this.CtxRachao.PlayerProfile.Any(x => x.PlayerProfileId.Equals(ticket.PlayerProfileId)))
                    throw new RachaoBusinessException(false, "O usuário vinculado à aprovação não existe mais");

                // verifica se o ticket é válido
                if(!IsValidTicket(ticket))
                    throw new RachaoBusinessException(false, "O e-mail de aprovação informado não é mais válido.");


                PlayerProfileStatus pps = new PlayerProfileStatus();
                pps.PlayerProfileId = ticket.PlayerProfileId;
                pps.LastStatusDate = DateTime.Now;
                if (isApproved)
                {                                        
                    pps.ProfileStatusId = STATUS_APROVADO;                    
                }
                else
                {
                    pps.ProfileStatusId  = STATUS_REPROVADO;                    
                }
                this.CtxRachao.PlayerProfileStatus.Add(pps);
                this.CtxRachao.SaveChanges();
            }                        

        }

        /// <summary>
        /// Verify if ticket is valid
        /// </summary>
        /// <param name="ticket">PlayerProfileApprovalTicket</param>
        /// <returns>
        /// True - ticket is valid
        /// False - ticket is invalid
        /// </returns>
        private bool IsValidTicket(PlayerProfileApprovalTicket ticket)
        {
            return this.CtxRachao.PlayerProfileApprovalTicket.Any(x=> x.GeneratedDate <= DateTime.Now 
                    && x.ValidDate >= DateTime.Now 
                    && x.PlayerProfileId == ticket.PlayerProfileId
                    && x.GeneratedDate.Equals(ticket.GeneratedDate)
                    && x.ValidDate.Equals(ticket.ValidDate));            
        }

        /// <summary>
        /// Verify if the last player profile status is pending
        /// </summary>
        /// <param name="pid">Player profile id</param>
        /// <returns>
        /// True - the last one is pending
        /// False - the last one isn't pending
        /// </returns>
        private bool IsPlayerProfileStatusPendente(int pid)
        {
            return (from x in this.CtxRachao.PlayerProfileStatus
                        where x.PlayerProfileId.Equals(pid)
                        orderby x.PlayerProfileStatusId descending
                        select x).Take(1).Any(y=> y.ProfileStatusId.Equals(STATUS_PENDENTE));
        }

        #endregion                


    }
}
